package ltd.nullpointer.tcp.core.resolver.parser;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.boot2.core.utils.ReflectUtil;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zhangweilin
 * @Description: 表达式计算
 * @date 2019/12/9
 */
@Component
public class CalcExpressionResolverParser implements ResolverParser<Object> {

    /**
     * 提取出所有的变量，如12+num1-age*height/90*(_last_n89ame65-$abc-(_ref+3.6))*math.pow(10, -3)/math.log10(abc),得到
     * num1
     * age
     * height
     * _last_n89ame65
     * $abc
     * _ref
     * math.pow
     * math.log10
     * abc
     */
    private final Pattern pattern = Pattern.compile("([a-zA-Z$_]\\w+)(\\.[a-zA-Z$_]\\w+)?");

    /**
     * 内置函数，不能被替换,故解析实体在给属性命名时，要避开函数名
     */
    private final List<String> formulaList = Arrays.asList("sysdate", "rand", "print", "println", "now", "long", "double", "str", "date_to_string",
            "string_to_date", "string.contains", "string.byteLength", "string.startsWith", "string.endsWith", "string.substring", "string.indexOf",
            "string.split", "string.join", "string.replace_first", "string.replace_all", "math.abs", "math.sqrt", "math.pow", "math.log", "math.log10",
            "math.sin", "math.cos", "math.tan", "map", "filter", "count", "include", "sort", "reduce", "seq.eq", "seq.neq", "seq.gt", "seq.ge", "seq.lt",
            "seq.le", "seq.nil", "seq.exists");

    /**
     * 按具体规则解析具体内容, 当前值用this表示
     *
     * @param byteArr
     * @return
     */
    @Override
    public Object resolve(byte[] byteArr, Object obj, Map<String, Object> paramMap) {
        Object value = paramMap.get("value");
        String expression = (String) paramMap.get("expression");
//        System.out.println("expression = " + expression);
        Map<String, Object> paramMap1 = new HashMap<>();
        Matcher ma = pattern.matcher(expression);
        while (ma.find()) {
//            System.out.println(ma.group());
            String fieldName = ma.group();
            //内置函数则跳过
            if (formulaList.contains(fieldName)) {
                continue;
            }

            if ("this".equalsIgnoreCase(fieldName)) {
                paramMap1.put("this", value);
            } else {
                //todo 以后解析复杂表达式，更要考虑用PropertyUtils.getNestedProperty,或考虑表达式引擎
                //读取类型xxx.bbb这样的表达式，而xxx从动态反射织入的_map中获取
                Object object = null;
                if (fieldName.contains("_")) {
                    String[] fieldNameArr = fieldName.split("_");
                    try {
                        Map<String, Object> map = (Map<String, Object>)  ReflectUtil.getFieldValue(obj, "_map");
                        object = map.get(fieldNameArr[0]);
                        object = PropertyUtils.getNestedProperty(object, fieldNameArr[1]);

                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }
                }else{
                    //直接从当前实例获取
                    object = ReflectUtil.getFieldValue(obj, fieldName);
                }
                if (null == object) {
                    return ReturnType.Delay;
                }
                paramMap1.put(fieldName, object);
            }
        }

//        String expression = "this*0.3M";
//        expression = expression.replace("this", "0.2");
        Expression compiledExp = AviatorEvaluator.compile(expression);
        Object result = compiledExp.execute(paramMap1);
        return result;
    }

    /**
     * 只在此类中使用，返回类型
     */
    public enum ReturnType {
        /**
         * 延迟计算，即首次遍历如果还有未计算的表达式，则延迟到后面计算
         */
        Delay,Continue;
    }
}
